Al ejecutar terraform plan o terraform apply con una lista provista para for_each , se produce un error que dice
Error: Invalid for_each argument on main.tf line 2, in resource "aws_ssm_parameter" "foo": 2: for_each = ["a", "b"] The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.Un ejemplo mínimo para reproducir este error es este:
resource "aws_ssm_parameter" "foo" { for_each = ["a", "b"] name = "foo-${each.value}" type = "String" value = "bar-${each.value}" } Este error a menudo se produce al pasar una lista a for_each , pero for_each solo funciona con tipos de datos no ordenados, es decir, con conjuntos y mapas.
La resolución depende de la situación.
Si la lista es solo una lista de cadenas, la solución más fácil es agregar una llamada a toset() para transformar la lista en un conjunto que puede ser manejado por for_each, como este
resource "aws_ssm_parameter" "foo" { for_each = toset(["a", "b"]) name = "foo-${each.value}" type = "String" value = "bar-${each.value}" }Si la entrada es una lista, pero se puede reorganizar fácilmente en un mapa, esta suele ser la mejor manera. Digamos que tenemos una lista como esta
locals { animals = [ { name = "Bello" age = 3 type = "dog" }, { name = "Minga" age = 4 type = "cat" }, ] }Entonces una reestructuración apropiada podría ser esta
locals { animals = { Bello : { age = 3 type = "dog" }, Minga : { age = 4 type = "cat" } } }que luego le permite definir
resource "aws_ssm_parameter" "foo" { for_each = local.animals name = each.key type = string value = "This is a ${each.value.type}, ${each.value.age} years old." } A veces es natural tener una lista, por ejemplo, proveniente de una salida de un módulo que uno no controla o de un recurso que se define con count . En tal situación, uno puede trabajar con un conteo como este
resource "aws_ssm_parameter" "foo" { count = length(local.my_list) name = my_list[count.index].name type = "String" value = my_list[count.index].value }que funciona para una lista de mapas que contienen nombre y valor como claves. Muchas veces, sin embargo, es más apropiado transformar la lista en un mapa como este
resource "aws_ssm_parameter" "foo" { for_each = { for x in local.my_list: x.id => x } name = each.value.name type = "String" value = each.value.value } Aquí uno debe elegir cualquier cosa apropiada en lugar de x.id Si my_list es una lista de objetos, normalmente hay algún campo común, como un nombre o una clave, que se puede usar. La ventaja de este enfoque a favor de usar count como se indicó anteriormente es que se comporta mejor al insertar o eliminar elementos de la lista. count no notará la inserción o eliminación como tal y, por lo tanto, actualizará todos los recursos siguiendo el lugar donde tuvo lugar la inserción, mientras que for_each realmente solo agrega o elimina el recurso con la identificación nueva o eliminada.